package lzc.encrypt.encryption;

import it.unisa.dia.gas.jpbc.Element;
import it.unisa.dia.gas.jpbc.Field;
import it.unisa.dia.gas.jpbc.Pairing;
import it.unisa.dia.gas.jpbc.PairingPreProcessing;
import it.unisa.dia.gas.plaf.jpbc.pairing.PairingFactory;

import java.io.Serializable;
import java.math.BigInteger;

/**
 * 映射参数类，IPPE中的双线性映射使用的参数
 *
 * @author 蓝色的三明治
 * @date 2024/09/19
 */
public class PairingParameter {
    // 生成元为大素数 g 的 p 阶循环群 G1
    private Field G1;
    private Element g;
    private BigInteger p;

    // p 阶循环群 G2，满足 G1 * G1 -> G2
    private Field G2;

    // 双线性映射关系e
    private Pairing e;

    // 该映射的有效域，即Z^*_p
    private Field Zp;

    /**
     * 初始化所有参数并记录
     */
    public PairingParameter() {
        // 生成双线性映射
        e = PairingFactory.getPairing("a.properties");
        PairingFactory.getInstance().setUsePBCWhenPossible(true); // 用于提高计算效率

        // 检查生成的是否是对称双线性映射
        if (!e.isSymmetric()) {
            throw new RuntimeException("密钥不对称");
        }

        G1 = e.getG1();
        G2 = e.getGT();

        g = G1.newRandomElement().getImmutable();// 生成 G1 的生成元
        p = G1.getOrder();// 获取 G1 的阶

        Zp = e.getZr();
    }

    /**
     * 从双线性映射的有效域中根据指定值获取Element对象
     *
     * @param i 指定值
     * @return {@link Element} 有效域中指定值对应的Element对象
     */
    public Element getFromZp(int i) {
        if (i == 0) {
            return Zp.newZeroElement();
        } else if (i == 1) {
            return Zp.newOneElement();
        } else {
            return Zp.newElement(i);
        }
    }

    /**
     * 从双线性映射的有效域中根据指定值获取Element对象
     *
     * @param i 指定值
     * @return {@link Element} 有效域中指定值对应的Element对象
     */
    public Element getFromZp(BigInteger i) {
        if (i.equals(BigInteger.ZERO)) {
            return Zp.newZeroElement();
        } else if (i.equals(BigInteger.ONE)) {
            return Zp.newOneElement();
        } else {
            return Zp.newElement(i);
        }
    }

    /**
     * 从双线性映射的有效域中获取随机Element对象
     *
     * @return {@link Element} 有效域中随机的Element对象
     */
    public Element getFromZp() {
        return Zp.newRandomElement();
    }

    /**
     * 进行双线性映射计算
     *
     * @param e1 元素1
     * @param e2 元素2
     * @return {@link Element} 计算结果 e(e1,e2)
     */
    public Element pairing(Element e1, Element e2) {
        return e.pairing(e1, e2);
    }

    /**
     * 进行双线性映射计算
     *
     * @param e1 元素向量1
     * @param e2 元素向量2
     * @return {@link Element} 计算结果 e(e1[0],e2[0])*...*e(e1[e1.length-1],e2[e2.length-1])
     */
    public Element pairing(Element[] e1, Element[] e2) {
        return e.pairing(e1, e2);
    }

    /**
     * 进行双线性映射计算的预计算，以 e1 作为第一个输入
     *
     * @param e1 双线性映射的第一个输入
     * @return {@link PairingPreProcessing} 双线性映射计算的预计算
     */
    public PairingPreProcessing pairing(Element e1) {
        return e.getPairingPreProcessingFromElement(e1);
    }

    public Element getG() {
        return g;
    }

    public Field getZp() {
        return Zp;
    }

    public Field getG2() {
        return G2;
    }

    public BigInteger getP() {
        return p;
    }
}
